
#include <linux/init.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/sched.h>
  #include <linux/unistd.h>
  #include <linux/delay.h>

#ifdef CONFIG_RALINK_GPIO_LED
#include <linux/timer.h>
#endif
//  #include <asm/unistd.h>
#include <asm/uaccess.h>
#include "ralink_gpio.h"

#include <asm/rt2880/surfboardint.h>

#ifdef  CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
static  devfs_handle_t devfs_handle;
#endif

//#define msleep(x) usleep(x*1000)
#define NAME			"ralink_gpio"
#define RALINK_GPIO_DEVNAME	"gpio"
int ralink_gpio_major = 252;
unsigned char ralink_gpio_irqnum = 0;
u32 ralink_gpio_intp = 0;
u32 ralink_gpio_edge = 0;
#if defined (RALINK_GPIO_HAS_2722)
u32 ralink_gpio2722_intp = 0;
u32 ralink_gpio2722_edge = 0;
#endif
ralink_gpio_reg_info ralink_gpio_info[RALINK_GPIO_NUMBER];
my_wiegand_value my_wiegand[DWWD_GATE_NUMBER] = {{1,GATE1_WIEGAND_D0,1,0},{2,GATE2_WIEGAND_D0,1,0},{0,GATE3_WIEGAND_D0,1,0},{1,GATE4_WIEGAND_D0,1,0}};	
my_gpio_value butten_value[DWWD_GATE_NUMBER+1] = {{GATE1_BUTTON,1},{GATE2_BUTTON,1},{GATE3_BUTTON,1},{GATE4_BUTTON,1},{ALARMIN,1}};
my_gpio_value sensor_value[3] = {{ALARMIN,GATE_CLOSE},{LOAD_DEFAULT,GATE_CLOSE},{GATE2_SENSOR,GATE_CLOSE}};													
extern unsigned long volatile jiffies;

ralink_gpio_reg_info info;
gpio_time_record record[RALINK_GPIO_NUMBER];

static unsigned long long wiegand[DWWD_GATE_NUMBER] = {1,1};
static unsigned char wiegandlen[DWWD_GATE_NUMBER] = {0,0};

//static spinlock_t     wiegand_lock;
//GPIO direction is in
void ralink_gpio_set_dir_in(unsigned int g_index)
{                                  
	unsigned long tmp;
	tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
	tmp &= ~(1<<g_index);
	*(volatile u32 *)(RALINK_REG_PIODIR) = cpu_to_le32(tmp);

}
//GPIO22-27 direction is in
void ralink_gpio2722_set_dir_in(unsigned int g_index)
{                               
        unsigned long tmp;
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));                 
		tmp &= ~cpu_to_le32(1<<(g_index-22));
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;
}
//GPIO direction is out	
void ralink_gpio_set_dir_out(unsigned int g_index)
{                                   
	unsigned long tmp;
	tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
	tmp |= (1<<g_index);
	*(volatile u32 *)(RALINK_REG_PIODIR) = cpu_to_le32(tmp);
}
//GPIO22-27 direction is out
void ralink_gpio2722_set_dir_out(unsigned int g_index)
{                                  
        unsigned long tmp;
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));                 
		tmp |= cpu_to_le32(1<<(g_index-22));
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;
}
//GPIO rising edge enable
void ralink_gpio_dis_piorena(unsigned int g_index)
{                                    
	     unsigned long tmp;
	     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIORENA));
	     tmp |=(1<<g_index);
	     *(volatile u32 *)(RALINK_REG_PIORENA) = cpu_to_le32(tmp);                            
}
//GPIO22-27 rising edge enable
void ralink_gpio2722_dis_piorena(unsigned int g_index)
{                              
	    unsigned long tmp;
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722RENA));
		tmp |= (0x1 << (g_index-22));
		*(volatile u32 *)(RALINK_REG_PIO2722RENA) = cpu_to_le32(tmp);
}
//GIO falling edge enable
void ralink_gpio_piofena(unsigned int g_index)
{                                   
	     unsigned long tmp;
	     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
	     tmp |= (1<<g_index);
	     *(volatile u32 *)(RALINK_REG_PIOFENA) = cpu_to_le32(tmp);                            
}
//GPIO22-27 falling edge enable
void ralink_gpio2722_piofena(unsigned int g_index)
{                               
	     unsigned long tmp;
	     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722FENA));
	     tmp |= (1<<(g_index-22));
	     *(volatile u32 *)(RALINK_REG_PIO2722FENA) = cpu_to_le32(tmp);                            
}
//GIO falling edge disable
void ralink_gpio_set_piofena(unsigned int g_index)
{                                   
	     unsigned long tmp;
	     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
	     tmp &= ~(1<<g_index);
	     *(volatile u32 *)(RALINK_REG_PIOFENA) = cpu_to_le32(tmp);                            
}
//GPIO22-27 falling edge disable
void ralink_gpio2722_set_piofena(unsigned int g_index)
{                               
	     unsigned long tmp;
	     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722FENA));
	     tmp &= ~(1<<(g_index-22));
	     *(volatile u32 *)(RALINK_REG_PIO2722FENA) = cpu_to_le32(tmp);                            
}
//write GPIO pin value		
void ralink_gpio_set_value(unsigned int g_index,int value)
{                          	
	unsigned long tmp;	
	tmp =le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));	
	if (value& 1L)
		tmp |= (1L <<g_index);
	else
		tmp &= ~(1L <<g_index);	
	*(volatile u32 *)(RALINK_REG_PIODATA)= cpu_to_le32(tmp);
}
//write GPIO pin22-27 value 
void ralink_gpio2722_set_value(unsigned int g_index,int value)
{                                
//write pin value
	unsigned long tmp;	
	tmp =le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));	
	if (value& 1L)
		tmp |= (1L <<(g_index-22));
	else
		tmp &= ~(1L <<(g_index-22));	
	*(volatile u32 *)(RALINK_REG_PIO2722DATA)= cpu_to_le32(tmp);
}
//read GPIO pin value
unsigned char ralink_gpio_get_value(unsigned int g_index)
{                                         	
	unsigned long tmp;	
	tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));	
	tmp = (tmp >> g_index) & 1L;	
	return (char)tmp&1L;
}
//read GPIO pin22-27 value
unsigned char ralink_gpio2722_get_value(unsigned int g_index)
{                      	
	unsigned long tmp;	   
	tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));	
	tmp = (tmp >> (g_index-22)) & 1L;	
	return (char)tmp&1L;
}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
long ralink_gpio_ioctl(struct file *file, unsigned int req,
		unsigned long arg)
#else
int ralink_gpio_ioctl(struct inode *inode, struct file *file, unsigned int req,
		unsigned long arg)
#endif
{
	unsigned long tmp;
	ralink_gpio_reg_info info;

	my_gpio_value my_vaule;
//	my_wiegand_value dwwiegand;
	req &= RALINK_GPIO_DATA_MASK;

	switch(req) 
	{	
	    case MY_WG_GPIO_IN:
		     copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
	         if(info.irq <=21)
			 {
			     ralink_gpio_set_dir_in(info.irq);
			     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
//			     printk(KERN_ERR  ":dir in gpio info.irq = %d  RALINK_REG_PIODIR = %lu %d\n",info.irq,tmp,__LINE__);
			 }else
			 {
			     ralink_gpio2722_set_dir_in(info.irq);
			     tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
//			     printk(KERN_ERR  ":dir in gpio2722 info.irq = %d  RALINK_REG_PIODIR = %lu %d\n",info.irq,tmp,__LINE__);
			 }
			 break;
			
		case MY_WG_GPIO_OUT:
	         copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
	         if(info.irq <=21)
			 {
			    ralink_gpio_set_dir_out(info.irq);
//			    tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
//			    printk(KERN_ERR  ":dir out gpio info.irq = %d  RALINK_REG_PIODIR = %lu \n",info.irq,tmp);
			 }else
			 {
		    	 ralink_gpio2722_set_dir_out(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
//		    	 printk(KERN_ERR  ":dir out gpio2722 info.irq = %d  RALINK_REG_PIO2722DIR = %lu \n",info.irq,tmp);
			 }
			 break;
			 
		case MY_WG_GPIO_DIS_PIOR:
             copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
	         if(info.irq<=21)
			 {
		    	 ralink_gpio_dis_piorena(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIORENA));
//		    	 printk(KERN_ERR  ":RALINK_REG_PIORENA = %lu gpio info.irq = %d \n",tmp,info.irq);
			 }else
			 {
		    	 ralink_gpio2722_dis_piorena(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722RENA));
//		    	 printk(KERN_ERR  ":RALINK_REG_PIO2722RENA = %lu gpio info.irq = %d \n",tmp,info.irq);
			 }
			 break;
		
        case MY_WG_GPIO_SET_PIOFENA:
		     copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
	         if(info.irq<=21)
			 {
		    	 ralink_gpio_piofena(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
//		    	 printk(KERN_ERR  ":RALINK_REG_PIOFENA = %lu , piofena write gpio info.irq = %d  \n",tmp,info.irq);
			 }else
			 {
		    	 ralink_gpio2722_piofena(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722FENA));
//		    	 printk(KERN_ERR  ":RALINK_REG_PIO2722FENA = %lu , piofena write gpio info.irq = %d  \n",tmp,info.irq);
			 }
			 break;
		
		case MY_WG_GPIO_PIOFENA:
		     copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
	         if(info.irq<=21)
			 {
		    	 ralink_gpio_set_piofena(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
//		    	 printk(KERN_ERR  ":RALINK_REG_PIOFENA = %lu , piofena write gpio info.irq = %d  \n",tmp,info.irq);
			 }else
			 {
		    	 ralink_gpio2722_set_piofena(info.irq);
//		    	 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722FENA));
//		    	 printk(KERN_ERR  ":RALINK_REG_PIO2722FENA = %lu , piofena write gpio info.irq = %d  \n",tmp,info.irq);
			 }
			 break;
			 
		case MY_WG_GPIO_GET0:
             wiegand[0] = my_wiegand[0].wiegand;
			 wiegandlen[0] = my_wiegand[0].wiegandlen;
			 copy_from_user(&my_wiegand[0], (my_wiegand_value *)arg, sizeof(my_wiegand[0]));
             my_wiegand[0].wiegand = wiegand[0];
             my_wiegand[0].wiegandlen = wiegandlen[0];		 
			 copy_to_user((void __user *)arg, &my_wiegand[0], sizeof(my_wiegand[0]));
//			 printk(KERN_ERR  "get2:gate=%d wgd0 = %d  wiegand=%d \n",dwwiegand.gate,dwwiegand.wgd0,dwwiegand.wiegand);
			 my_wiegand[0].wiegandlen = 0;
			 my_wiegand[0].wiegand = 1;
			 break;
		
        case MY_WG_GPIO_GET1:
             wiegand[1] = my_wiegand[1].wiegand;
			 wiegandlen[1] = my_wiegand[1].wiegandlen;
			 copy_from_user(&my_wiegand[1], (my_wiegand_value *)arg, sizeof(my_wiegand[1]));	
             my_wiegand[1].wiegand = wiegand[1];
             my_wiegand[1].wiegandlen = wiegandlen[1];			 
             copy_to_user((void __user *)arg, &my_wiegand[1], sizeof(my_wiegand[1]));			
             my_wiegand[1].wiegandlen = 0;
			 my_wiegand[1].wiegand = 1;
			 break;
			 
		case MY_WG_GPIO_GET2:
             wiegand[2] = my_wiegand[2].wiegand;
			 wiegandlen[2] = my_wiegand[2].wiegandlen;
			 copy_from_user(&my_wiegand[2], (my_wiegand_value *)arg, sizeof(my_wiegand[2]));	
             my_wiegand[2].wiegand = wiegand[2];
             my_wiegand[2].wiegandlen = wiegandlen[2];			 
             copy_to_user((void __user *)arg, &my_wiegand[2], sizeof(my_wiegand[2]));			
             my_wiegand[2].wiegandlen = 0;
			 my_wiegand[2].wiegand = 1;
			 break;	 
			
		case MY_WG_GPIO_GET3:
             wiegand[3] = my_wiegand[3].wiegand;
			 wiegandlen[3] = my_wiegand[3].wiegandlen;
			 copy_from_user(&my_wiegand[3], (my_wiegand_value *)arg, sizeof(my_wiegand[3]));	
             my_wiegand[3].wiegand = wiegand[3];
             my_wiegand[3].wiegandlen = wiegandlen[3];			 
             copy_to_user((void __user *)arg, &my_wiegand[3], sizeof(my_wiegand[3]));			
             my_wiegand[3].wiegandlen = 0;
			 my_wiegand[3].wiegand = 1;
			 break;	
			
		case MY_WG_GPIO_REG_IRQ:
             copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));			 
			 if (0 <= info.irq && info.irq < RALINK_GPIO_NUMBER) 
			 {
		    	  ralink_gpio_info[info.irq].pid = info.pid;
//		    	  printk(KERN_ERR NAME ": irq number=%d, info.pid=%d %d\n",info.irq,info.pid,__LINE__);
			 }else
			 {
//		     	 printk(KERN_ERR NAME ": irq number(%d) out of range %d\n",info.irq,__LINE__);
			 }
		     break;
			
		case DWWD_GPIO_INTP_CLEAR:
             copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));	
			 if (info.irq<=21){
			 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOINT));
			 tmp |=(1<< info.irq);
             *(volatile u32 *)(RALINK_REG_PIOINT) = cpu_to_le32(tmp);	
        	 }else{
			 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722INT));
			 tmp |=(1<< (info.irq-22));
             *(volatile u32 *)(RALINK_REG_PIO2722INT) = cpu_to_le32(tmp);	
        	 }
			 break;
			 
		case DWWD_GPIO_PIOEDGE_CLEAR:
             copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));	
			 if (info.irq<=21){
			 ralink_gpio_edge = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOEDGE));
			 tmp |=(1<< info.irq);
             *(volatile u32 *)(RALINK_REG_PIOEDGE) = cpu_to_le32(tmp);	
			  }else{
			 tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722EDGE));
			 tmp |=(1<< (info.irq-22));
             *(volatile u32 *)(RALINK_REG_PIO2722EDGE) = cpu_to_le32(tmp);	
			  } 
			 break;
			 
	    case DWWD_GPIO_SET:
			copy_from_user(&my_vaule, (my_gpio_value *)arg, sizeof(my_vaule));
//			printk(KERN_ERR  ":ralink write gpio idx = %d  vuale = %d\n",my_vaule.index,my_vaule.vaule);
			if(my_vaule.index <= 21)
			{
			    ralink_gpio_set_value(my_vaule.index,my_vaule.vaule);
			}else
			{
			    ralink_gpio2722_set_value(my_vaule.index,my_vaule.vaule);
			}
			break;
			
		case DWWD_GPIO_GET:
			copy_from_user(&my_vaule, (my_gpio_value *)arg, sizeof(my_vaule));
//			printk(KERN_ERR  ":ralink get(1) gpio idx = %d  vuale = %d\n",my_vaule.index,my_vaule.vaule);
			if(my_vaule.index <= 21){
			my_vaule.vaule = ralink_gpio_get_value(my_vaule.index);		
			}else{
			my_vaule.vaule = ralink_gpio2722_get_value(my_vaule.index);
			}
			copy_to_user((void __user *)arg, &my_vaule, sizeof(my_vaule));
//			printk(KERN_ERR  ":ralink get(2) gpio idx = %d  vuale = %d\n",my_vaule.index,my_vaule.vaule);
			break;
		
		case DWWD_GPIO_REG_IRQ:
			copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
			if (0 <= info.irq && info.irq < RALINK_GPIO_NUMBER) 
			{
				ralink_gpio_info[info.irq].pid = info.pid;
	#if defined (RALINK_GPIO_HAS_2722)
				if (info.irq <= 21) 
				{
					tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIORENA));
					tmp |= (0x1 << info.irq);
					*(volatile u32 *)(RALINK_REG_PIORENA) = cpu_to_le32(tmp);
//					tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
//					tmp |= (0x1 << info.irq);
//					*(volatile u32 *)(RALINK_REG_PIOFENA) = cpu_to_le32(tmp);
				}
				else 
				{
					tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722RENA));
					tmp |= (0x1 << (info.irq-22));
					*(volatile u32 *)(RALINK_REG_PIO2722RENA) = cpu_to_le32(tmp);
//					tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722FENA));
//					tmp |= (0x1 << (info.irq-22));
//					*(volatile u32 *)(RALINK_REG_PIO2722FENA) = cpu_to_le32(tmp);
				}
	#else
				if (info.irq <= 23) 
				{
					tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIORENA));
					tmp |= (0x1 << info.irq);
					*(volatile u32 *)(RALINK_REG_PIORENA) = cpu_to_le32(tmp);
//					tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
//					tmp |= (0x1 << info.irq);
//					*(volatile u32 *)(RALINK_REG_PIOFENA) = cpu_to_le32(tmp);
				}
	#endif
	//			printk(KERN_ERR NAME ": irq number(%d) info.pid:%d %d\n",info.irq,info.pid,__LINE__);
			}
			else
			{
				printk(KERN_ERR NAME ": irq number(%d) out of range %d\n",info.irq,__LINE__);
			}
			break;
		
	case RALINK_GPIO_SET_DIR:
		*(volatile u32 *)(RALINK_REG_PIODIR) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO_SET_DIR_IN:
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
		tmp &= ~cpu_to_le32(arg);
		*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;		
		break;
	case RALINK_GPIO_SET_DIR_OUT:
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODIR));
		tmp |= cpu_to_le32(arg);
		*(volatile u32 *)(RALINK_REG_PIODIR) = tmp;
		break;
	case RALINK_GPIO_READ: //RALINK_GPIO_READ_INT
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIODATA));
		put_user(tmp, (int __user *)arg);
		break;
	case RALINK_GPIO_WRITE: //RALINK_GPIO_WRITE_INT
		*(volatile u32 *)(RALINK_REG_PIODATA) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO_SET: //RALINK_GPIO_SET_INT
		*(volatile u32 *)(RALINK_REG_PIOSET) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO_CLEAR: //RALINK_GPIO_CLEAR_INT
		*(volatile u32 *)(RALINK_REG_PIORESET) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO_ENABLE_INTP:
		*(volatile u32 *)(RALINK_REG_INTENA) = cpu_to_le32(RALINK_INTCTL_PIO);
		break;
	case RALINK_GPIO_DISABLE_INTP:
		*(volatile u32 *)(RALINK_REG_INTDIS) = cpu_to_le32(RALINK_INTCTL_PIO);
		break;
	case RALINK_GPIO_REG_IRQ:
		copy_from_user(&info, (ralink_gpio_reg_info *)arg, sizeof(info));
		if (0 <= info.irq && info.irq < RALINK_GPIO_NUMBER) 
		{
			ralink_gpio_info[info.irq].pid = info.pid;
#if defined (RALINK_GPIO_HAS_2722)
			if (info.irq <= 21) 
			{
				tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIORENA));
				tmp |= (0x1 << info.irq);
				*(volatile u32 *)(RALINK_REG_PIORENA) = cpu_to_le32(tmp);
				tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
				tmp |= (0x1 << info.irq);
				*(volatile u32 *)(RALINK_REG_PIOFENA) = cpu_to_le32(tmp);
			}
			else 
			{
				tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722RENA));
				tmp |= (0x1 << (info.irq-22));
				*(volatile u32 *)(RALINK_REG_PIO2722RENA) = cpu_to_le32(tmp);
				tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722FENA));
				tmp |= (0x1 << (info.irq-22));
				*(volatile u32 *)(RALINK_REG_PIO2722FENA) = cpu_to_le32(tmp);
			}
#else
			if (info.irq <= 23) 
			{
				tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIORENA));
				tmp |= (0x1 << info.irq);
				*(volatile u32 *)(RALINK_REG_PIORENA) = cpu_to_le32(tmp);
				tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOFENA));
				tmp |= (0x1 << info.irq);
				*(volatile u32 *)(RALINK_REG_PIOFENA) = cpu_to_le32(tmp);
			}
#endif
//			printk(KERN_ERR NAME ": irq number(%d) info.pid:%d %d\n",info.irq,info.pid,__LINE__);
		}
		else
		{
			printk(KERN_ERR NAME ": irq number(%d) out of range %d\n",info.irq,__LINE__);
		}
		break;

#if defined (RALINK_GPIO_HAS_2722)
	case RALINK_GPIO2722_SET_DIR:
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO2722_SET_DIR_IN:
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
		tmp &= ~cpu_to_le32(arg);
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;
//		printk(KERN_ERR  ":ralink set in gpio arg = %lu  \n",arg);
		break;
	case RALINK_GPIO2722_SET_DIR_OUT:
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DIR));
		tmp |= cpu_to_le32(arg);
		*(volatile u32 *)(RALINK_REG_PIO2722DIR) = tmp;
		break;
	case RALINK_GPIO2722_READ:
		tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722DATA));
		put_user(tmp, (int __user *)arg);
		break;
	case RALINK_GPIO2722_WRITE:
		*(volatile u32 *)(RALINK_REG_PIO2722DATA) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO2722_SET:
		*(volatile u32 *)(RALINK_REG_PIO2722SET) = cpu_to_le32(arg);
		break;
	case RALINK_GPIO2722_CLEAR:
		*(volatile u32 *)(RALINK_REG_PIO2722SET) = cpu_to_le32(arg);
		break;
#endif
	default:
		return -ENOIOCTLCMD;
	}
	return 0;
}

int ralink_gpio_open(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MOD_INC_USE_COUNT;
#else
	try_module_get(THIS_MODULE);
#endif
	return 0;
}

int ralink_gpio_release(struct inode *inode, struct file *file)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MOD_DEC_USE_COUNT;
#else
	module_put(THIS_MODULE);
#endif
	return 0;
}

struct file_operations ralink_gpio_fops =
{
	owner:		THIS_MODULE,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
	unlocked_ioctl:	ralink_gpio_ioctl,
#else
	ioctl:		ralink_gpio_ioctl,
#endif
	open:		ralink_gpio_open,
	release:	ralink_gpio_release,
};

int __init ralink_gpio_init(void)
{
	unsigned int i;
	u32 gpiomode;
//    unsigned int g_index;
	
#ifdef  CONFIG_DEVFS_FS
	if (devfs_register_chrdev(ralink_gpio_major, RALINK_GPIO_DEVNAME,&ralink_gpio_fops)) 
	{
		printk(KERN_ERR NAME ": unable to register character device %d\n",__LINE__);
		return -EIO;
	}
	devfs_handle = devfs_register(NULL, RALINK_GPIO_DEVNAME,
			DEVFS_FL_DEFAULT, ralink_gpio_major, 0,
			S_IFCHR | S_IRUGO | S_IWUGO, &ralink_gpio_fops, NULL);
#else
	int r = 0;
	r = register_chrdev(ralink_gpio_major, RALINK_GPIO_DEVNAME,&ralink_gpio_fops);
	if (r < 0) 
	{
		printk(KERN_ERR NAME ": unable to register character device %d\n",__LINE__);
		return r;
	}
	if (ralink_gpio_major == 0) 
	{
		ralink_gpio_major = r;
		printk(KERN_DEBUG NAME ": got dynamic major %d %d\n", r,__LINE__);
	}
#endif

//config these pins to gpio mode
	gpiomode = le32_to_cpu(*(volatile u32 *)(RALINK_REG_GPIOMODE));
//	printk(KERN_ERR NAME ":RALINK_REG_GPIOMODE = %d \n",gpiomode);
// config JTAG is GPIO mode and I2C\SPI\UARTL\UARTF\I2S is normal  
//	gpiomode &= ~0x60c07f; 
//    gpiomode |= 0x20404c;
//	printk(KERN_ERR NAME " :RALINK_REG_GPIOMODE 2 = %0x \n",gpiomode);
//	gpiomode |= RALINK_GPIOMODE_DFT;
#define UARTL_IS_NORMAL
#ifdef UARTL_IS_NORMAL                 // config JTAG\UARTF is GPIO mode and I2C\SPI\UARTL is normal
  	gpiomode &= ~0x5c;
    gpiomode |= 0x5c;
#elif defined UARTL_IS_GPIO            // config JTAG\UARTF\UARTL is GPIO mode and I2C\SPI is normal  
    gpiomode &= ~0x7c;
    gpiomode |= 0x7c;
#endif
//	printk(KERN_ERR NAME " :RALINK_REG_GPIOMODE 0 = %d \n",gpiomode);
    gpiomode &= ~0x4000;
    gpiomode |= 0x4000;
//	printk(KERN_ERR NAME " :RALINK_REG_GPIOMODE 1 = %d \n",gpiomode);
	gpiomode &= ~0x400000;
    gpiomode |= 0x400000;

	*(volatile u32 *)(RALINK_REG_GPIOMODE) = cpu_to_le32(gpiomode);
//    printk(KERN_ERR NAME " :RALINK_REG_GPIOMODE 3 = %0x %d\n",gpiomode,__LINE__);
	
//enable gpio interrupt
	*(volatile u32 *)(RALINK_REG_INTENA) = cpu_to_le32(RALINK_INTCTL_PIO);
	
//	printk(KERN_ERR NAME ":RALINK_INTCTL_PIO = %d \n",RALINK_INTCTL_PIO);
	
	for (i = 0; i < RALINK_GPIO_NUMBER; i++) {
		ralink_gpio_info[i].irq = i;
		ralink_gpio_info[i].pid = 0;
	}
    
#ifdef CONFIG_RALINK_GPIO_LED
	ralink_gpio_led_init_timer();
#endif
//	printk("Ralink gpio driver initialized\n");
	return 0;
}

void __exit ralink_gpio_exit(void)
{
#ifdef  CONFIG_DEVFS_FS
	devfs_unregister_chrdev(ralink_gpio_major, RALINK_GPIO_DEVNAME);
	devfs_unregister(devfs_handle);
#else
	unregister_chrdev(ralink_gpio_major, RALINK_GPIO_DEVNAME);
#endif

	//config these pins to normal mode
	*(volatile u32 *)(RALINK_REG_GPIOMODE) &= ~RALINK_GPIOMODE_DFT;
	//disable gpio interrupt
	*(volatile u32 *)(RALINK_REG_INTDIS) = cpu_to_le32(RALINK_INTCTL_PIO);
	
//	printk("Ralink gpio driver exited\n");
}

/*
 * 1. save the PIOINT and PIOEDGE value
 * 2. clear PIOINT by writing 1
 * (called by interrupt handler)
 */
void ralink_gpio_save_clear_intp(void)
{
	ralink_gpio_intp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOINT));
	ralink_gpio_edge = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIOEDGE));
	*(volatile u32 *)(RALINK_REG_PIOINT) = cpu_to_le32(0x00FFFFFF);
	*(volatile u32 *)(RALINK_REG_PIOEDGE) = cpu_to_le32(0x00FFFFFF);      
#if defined (RALINK_GPIO_HAS_2722)
	ralink_gpio2722_intp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722INT));
	ralink_gpio2722_edge = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO2722EDGE));
	*(volatile u32 *)(RALINK_REG_PIO2722INT) = cpu_to_le32(0x0000FFFF);
	*(volatile u32 *)(RALINK_REG_PIO2722EDGE) = cpu_to_le32(0x0000FFFF);
#endif
}

/*
 * send a signal(SIGRTMIN+9,10,11,12) to the registered user process whenever any gpio
 * interrupt comes
 * (called by interrupt handler)
 */
void ralink_gpio_notify_user(unsigned char usr)
{
//	spin_lock(&wiegand_lock);		
	unsigned long now;
    
	struct task_struct *p = NULL;

	if (RALINK_GPIO_NUMBER <= ralink_gpio_irqnum)
	{
		printk(KERN_ERR NAME ": gpio irq number out of range %d\n",__LINE__);
		return;
	}

	//don't send any signal if pid is 0 or 1
	if ((int)ralink_gpio_info[ralink_gpio_irqnum].pid < 2)
		return;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,35)
	p = find_task_by_vpid(ralink_gpio_info[ralink_gpio_irqnum].pid);
#else
	p = find_task_by_pid(ralink_gpio_info[ralink_gpio_irqnum].pid);
#endif

	if (NULL == p) 
	{
		printk(KERN_ERR NAME ": no registered process to notify ralink_gpio_irqnum:%d  ralink_gpio_info[%d].pid %d\n",ralink_gpio_irqnum,ralink_gpio_irqnum,ralink_gpio_info[ralink_gpio_irqnum].pid,__LINE__);
		return;
	}

	if (usr == 1) 
	{
        if((my_wiegand[0].wiegandlen==1)&&((ralink_gpio_irqnum == my_wiegand[0].wgd0) || (ralink_gpio_irqnum == my_wiegand[0].wgd0 +1)))
		{
		    send_sig(SIGRTMIN+11, p, 0);
//			printk(KERN_ERR "SIGRTMIN+11 %d\n",__LINE__);
		}
		if((my_wiegand[1].wiegandlen==1)&&((ralink_gpio_irqnum == my_wiegand[1].wgd0) || (ralink_gpio_irqnum == my_wiegand[1].wgd0 +1)))
		{
		    send_sig(SIGRTMIN+12, p, 0);
		}
		if((my_wiegand[2].wiegandlen==1)&&((ralink_gpio_irqnum == my_wiegand[2].wgd0) || (ralink_gpio_irqnum == my_wiegand[2].wgd0 +1)))
		{
		    send_sig(SIGRTMIN+13, p, 0);
//			printk(KERN_ERR "SIGRTMIN+11 %d\n",__LINE__);
		}
		if((my_wiegand[3].wiegandlen==1)&&((ralink_gpio_irqnum == my_wiegand[3].wgd0) || (ralink_gpio_irqnum == my_wiegand[3].wgd0 +1)))
		{
		    send_sig(SIGRTMIN+14, p, 0);
		}				   
	}
	else if (usr == 2) 
	{
//	    now = jiffies;
		if(ralink_gpio_edge & (1 << ralink_gpio_irqnum))                                   //rising edge
	    {    
            send_sig(SIGRTMIN+9, p, 0);
//			printk(KERN_NOTICE NAME ": sending a SIGRTMIN+9 to irqnum %d %d\n",(int)ralink_gpio_irqnum,__LINE__);
	    }
	}
	else if (usr == 4) 
	{		    
	    now = jiffies;
    	if (ralink_gpio_edge & (1 << ralink_gpio_irqnum)) 
		{				
//	        now = jiffies;
//        printk(KERN_NOTICE NAME ":process:%lu recordrising:%lu \n",now,record[ralink_gpio_irqnum].rising);		
//			if((ralink_gpio_irqnum>=22) && (ralink_gpio_irqnum <28) && (ralink_gpio2722_edge & (1 << (ralink_gpio_irqnum - 22))))  //rising edge  
//			{                                    				
	//			printk(KERN_NOTICE NAME ":processrising:%lu recordrising:%lu \n",now,record[ralink_gpio_irqnum].rising);
	/*		if (record[ralink_gpio_irqnum].rising != 0 && time_before_eq(now,
					record[ralink_gpio_irqnum].rising + 40L))          
		    {
//					unsigned long  now0=now;
//					printk(KERN_NOTICE NAME ":process0:%lu \n",now0);
					/*
					 * If the interrupt comes in a short period,
					 * it might be floating. We ignore it.
					 */
/*			}else
			{
//	       		unsigned long  now1=now;
//				printk(KERN_NOTICE NAME ":process1:%lu \n",now1);
//				record[ralink_gpio_irqnum].rising = now;		    	
			}*/
			record[ralink_gpio_irqnum].rising = now;
	        printk(KERN_NOTICE NAME ":processrisingEND:%lu recordrising:%lu %d\n",now,record[ralink_gpio_irqnum].rising,__LINE__);			
		}else                                 //falling edge
		{		    						
//			unsigned long nowf=now;
//			printk(KERN_NOTICE NAME ":processfalling:%lu recordrising:%lu +800L:%lu \n",nowf,record[ralink_gpio_irqnum].rising,record[ralink_gpio_irqnum].rising + 800L);
			if (time_before(now, record[ralink_gpio_irqnum].rising + 800L))     //one click
			{                   
//				unsigned long  now3=now;
//				printk(KERN_NOTICE NAME ":process2:%lu \n",now3);
                record[ralink_gpio_irqnum].rising = now;
                printk(KERN_NOTICE NAME ":process2:%lu %d\n",now,__LINE__);				
			}else                                                    //press for several seconds
			{
//				unsigned long  now2=now;
		        send_sig(SIGUSR2, p, 0);
		        printk(KERN_NOTICE NAME ": sending a SIGUSR2 to process: %d %d\n",(int)ralink_gpio_info[ralink_gpio_irqnum].pid,__LINE__);										
			}
//            printk(KERN_NOTICE NAME ":processfallingEND:%lu recordrising:%lu +800L:%lu \n",nowf,record[ralink_gpio_irqnum].rising,record[ralink_gpio_irqnum].rising + 800L);			
	    }
//		}
	}else if (usr == 6) 
	{
		if (ralink_gpio_edge & (1 << ralink_gpio_irqnum)) 
		{
			send_sig(SIGRTMIN+7, p, 0);
			printk(KERN_NOTICE NAME ": sending a SIGRTMIN+7 to irqnum %d %d\n",(int)ralink_gpio_irqnum,__LINE__);
		}
	}else if (usr == 7) 
	{
		if (ralink_gpio_edge & (1 << ralink_gpio_irqnum)) 
		{
			send_sig(SIGRTMIN+6, p, 0);
			printk(KERN_NOTICE NAME ": sending a SIGRTMIN+6 to irqnum %d %d\n",(int)ralink_gpio_irqnum,__LINE__);
		}
	}else if (usr == 8) 
	{
		if (ralink_gpio_edge & (1 << ralink_gpio_irqnum)) 
		{
			send_sig(SIGRTMIN+15, p, 0);
			printk(KERN_NOTICE NAME ": sending a SIGRTMIN+15 to irqnum %d %d\n",(int)ralink_gpio_irqnum,__LINE__);
		}
	}
#if defined (RALINK_GPIO_HAS_2722)
    else if (usr == 3) 
	{
        send_sig(SIGRTMIN+10, p, 0);
		printk(KERN_NOTICE NAME ": sending a SIGRTMIN+10 to irqnum %d %d\n",(int)ralink_gpio_irqnum,__LINE__);
	}else if (usr == 5) 
	{		    
		now = jiffies;
    	if ((ralink_gpio_irqnum>=22) && (ralink_gpio_irqnum <28)&& (ralink_gpio2722_edge & (1 << (ralink_gpio_irqnum - 22)))) 
		{
		    if (record[ralink_gpio_irqnum].rising != 0 && time_before_eq(now,
				record[ralink_gpio_irqnum].rising + 200L)) 
			{
			    /*
				 * If the interrupt comes in a short period,
				 * it might be floating. We ignore it.
				 */				
		    }else
			{
    			record[ralink_gpio_irqnum].rising = now;
	        	if (time_before(now, record[ralink_gpio_irqnum].falling + 600L)) 
				{							
		    	    send_sig(SIGRTMIN+8, p, 0);
//					printk(KERN_NOTICE NAME ": sending a SIGRTMIN+8 to irqnum %d\n",
//        		        (int)ralink_gpio_irqnum);
                }else
			    {
//				    send_sig(SIGRTMAX-11, p, 0);
//				    printk(KERN_NOTICE NAME ": BUTTON1 unreleas. irqnum %d\n",
//        		    (int)ralink_gpio_irqnum);
			    }
			}	
		}else 
	    {
		    record[ralink_gpio_irqnum].falling = now;
        }
	}		
#endif	
//	spin_unlock(&wiegand_lock);	  
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
void ralink_gpio_irq_handler(unsigned int irq, struct irqaction *irqaction)
#else
irqreturn_t ralink_gpio_irq_handler(int irq, void *irqaction)
#endif
{
	unsigned char i;
	unsigned char n;
	
	ralink_gpio_save_clear_intp();
	
	for (i = 0; i <= 21; i++) 
	{
		if (! (ralink_gpio_intp & (1 << i)))
		{
			continue;
		}else
		{
		    ralink_gpio_irqnum = i;
		    for(n=0;n<DWWD_GATE_NUMBER;n++)
		    {
                if ((ralink_gpio_edge & (0x1 << (my_wiegand[n].wgd0 +1)))&& (ralink_gpio_irqnum == (my_wiegand[n].wgd0 +1)))
				{		
				    my_wiegand[n].wiegand = (my_wiegand[n].wiegand<<1)+1;
					my_wiegand[n].wiegandlen++;
                    ralink_gpio_notify_user(1);
      			}

		        if ((ralink_gpio_edge & (0x1 << my_wiegand[n].wgd0))&& (ralink_gpio_irqnum == my_wiegand[n].wgd0))
				{
				    my_wiegand[n].wiegand = my_wiegand[n].wiegand<<1;
					my_wiegand[n].wiegandlen++;
                    ralink_gpio_notify_user(1);
                }
            }
            if(ralink_gpio_irqnum == butten_value[1].index)
		    {				
		        ralink_gpio_notify_user(2);
//		        printk(KERN_ERR "butten_value[1].index=%d,ralink_gpio_notify_user(2) %d\n",ralink_gpio_irqnum,__LINE__);
		    }
            if(ralink_gpio_irqnum == LOAD_DEFAULT)
			{				
			    ralink_gpio_notify_user(4);
				printk(KERN_ERR ":loaddefault[%d].index,ralink_gpio_notify_user(4)\n",i);
		    }
		    if(ralink_gpio_irqnum == butten_value[2].index)
		    {				
		        ralink_gpio_notify_user(6);
//		        printk(KERN_ERR "butten_value[1].index=%d,ralink_gpio_notify_user(2) %d\n",ralink_gpio_irqnum,__LINE__);
		    }
		    if(ralink_gpio_irqnum == butten_value[3].index)
			{				
			    ralink_gpio_notify_user(7);
//				printk(KERN_ERR "butten_value[0].index=%d,ralink_gpio_notify_user(4) %d\n",ralink_gpio_irqnum,__LINE__);
		    }
		    if(ralink_gpio_irqnum == sensor_value[2].index)
			{				
			    ralink_gpio_notify_user(8);
//				printk(KERN_ERR "butten_value[0].index=%d,ralink_gpio_notify_user(4) %d\n",ralink_gpio_irqnum,__LINE__);
		    }
/*		    if(ralink_gpio_irqnum == LOAD_DEFAULT)
		    {		        				
				ralink_gpio_notify_user(3);
//				printk(KERN_ERR ":loaddefault[%d].index,ralink_gpio_notify_user(3)\n",i);
		    }*/				        
       	}
        break;
    }
#if defined (RALINK_GPIO_HAS_2722)	
	for (i = 22; i < 28; i++)                                                               // handle if >22 
    {                                                                
		if (! (ralink_gpio2722_intp & (1 << (i - 22))))
        {
			continue;
		}else
		{		    
			ralink_gpio_irqnum = i;
			if(ralink_gpio_irqnum == butten_value[0].index)
		    {		        				
				ralink_gpio_notify_user(3);
				printk(KERN_ERR ">22: butten_value[0].index=%d,ralink_gpio_notify_user(3) %d\n",ralink_gpio_irqnum,__LINE__);
//				
		    }
            if(ralink_gpio_irqnum == ALARMIN)
			{				
			    ralink_gpio_notify_user(5);
//				printk(KERN_ERR ">22:butten_value[0].index=%d,ralink_gpio_notify_user(4)\n",ralink_gpio_irqnum);
		    }
	    }
	break;
	}  			
#endif
	
	return IRQ_HANDLED;
}

struct irqaction ralink_gpio_irqaction = {
	.handler = ralink_gpio_irq_handler,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
	.flags = IRQF_DISABLED,
#else
	.flags = SA_INTERRUPT,
#endif
	.name = "ralink_gpio",
};

void __init ralink_gpio_init_irq(void)
{
	setup_irq(SURFBOARDINT_GPIO, &ralink_gpio_irqaction);
//	printk(KERN_ERR NAME" irqaction=%s,SURFBOARDINT_GPIO =%d, flags = %d %d\n",
//	                 ralink_gpio_irqaction.name,SURFBOARDINT_GPIO,ralink_gpio_irqaction.flags,__LINE__);
}

MODULE_DESCRIPTION("DW SoC GPIO Driver");
MODULE_AUTHOR("hxg@dawutech.cn");
MODULE_LICENSE("GPL");

module_init(ralink_gpio_init);
module_exit(ralink_gpio_exit);

